package com.franklin.ideaplugin.maphelper.col.operation;

import cn.hutool.core.map.SafeConcurrentHashMap;
import com.franklin.ideaplugin.api.utils.psi.PsiClassUtils;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.*;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.deque.BlockingDequeReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.deque.DequeReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.list.*;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.queue.BlockingQueueReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.queue.GuavaQueuesCreateOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.queue.QueueReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.set.GuavaSetsCreateOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.set.JdkSetCreateOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.set.SetReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.collection.set.SortedSetReadWriteOperation;
import com.franklin.ideaplugin.maphelper.col.operation.impl.map.*;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

/**
 * @author Ye Junhui
 * @since 2024/3/11
 */
public class ColOperationFactory {

    /**
     * 集合操作，由低级到高级
     */
    public final static List<IColReadWriteOperation> COLLECTION_READ_WRITE_OPERATIONS = Lists.newArrayList(
            //deque
            buildReadWriteProxy(new BlockingDequeReadWriteOperation()),
            buildReadWriteProxy(new DequeReadWriteOperation()),
            //queue
            buildReadWriteProxy(new BlockingQueueReadWriteOperation()),
            buildReadWriteProxy(new QueueReadWriteOperation()),
            //fastjson
            buildReadWriteProxy(new FastJson1JsonArrayReadWriteOperation()),
            buildReadWriteProxy(new FastJson2JsonArrayReadWriteOperation()),
            //list
            buildReadWriteProxy(new ListReadWriteOperation()),
            //set
            buildReadWriteProxy(new SortedSetReadWriteOperation()),
            buildReadWriteProxy(new SetReadWriteOperation()),
            //collection
            buildReadWriteProxy(new CollectionReadWriteOperation())
    );
    public final static List<IColCreateOperation> COLLECTION_CREATE_OPERATIONS = Lists.newArrayList(
            //list
            buildCreateProxy(new JdkListCreateOperation()),
            buildCreateProxy(new JdkCollectionsCollectionCreateOperation()),
            buildCreateProxy(new JdkArraysCreateOperation()),
            buildCreateProxy(new GuavaListsCreateOperation()),
            //set
            buildCreateProxy(new JdkSetCreateOperation()),
            buildCreateProxy(new GuavaSetsCreateOperation()),
            //queue
            buildCreateProxy(new GuavaQueuesCreateOperation())
    );

    /**
     * map操作
     */
    public final static List<IColReadWriteOperation> MAP_READ_WRITE_OPERATIONS = Lists.newArrayList(
            //fastJson
            buildReadWriteProxy(new FastJson1JsonObjectReadWriteOperation()),
            buildReadWriteProxy(new FastJson2JsonObjectReadWriteOperation()),
            //map
            buildReadWriteProxy(new SortedMapReadWriteOperation()),
            buildReadWriteProxy(new MapReadWriteOperation())
    );
    public final static List<IColCreateOperation> MAP_CREATE_OPERATIONS = Lists.newArrayList(
        buildCreateProxy(new JdkMapCreateOperation()),
        buildCreateProxy(new GuavaMapsCreateOperation())
    );

    public static ColOperationFactory getInstance(){
        return ApplicationManager.getApplication().getService(ColOperationFactory.class);
    }

    /**
     * 构建读写代理对象
     *
     * @param colReadWriteOperation
     * @return
     */
    private static IColReadWriteOperation buildReadWriteProxy(IColReadWriteOperation colReadWriteOperation) {
        return buildProxy(colReadWriteOperation, IColReadWriteOperation.class);
    }

    /**
     * 构建创建代理对象
     *
     * @param colCreateOperation
     * @return
     */
    private static IColCreateOperation buildCreateProxy(IColCreateOperation colCreateOperation) {
        return buildProxy(colCreateOperation, IColCreateOperation.class);
    }

    /**
     * 构建代理对象
     *
     * @param colOperation
     * @return
     */
    private static <T extends IColOperation> T buildProxy(T colOperation,Class<T> interfaceClazz) {
        Object proxyInstance = Proxy.newProxyInstance(
                ColOperationFactory.class.getClassLoader(),
                new Class[]{interfaceClazz},
                new InvocationHandler() {

                    /**
                     * 缓存
                     */
                    private final static Map<Class<?>, Map<Method, Object>> cacheMap = new SafeConcurrentHashMap<>();

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Map<Method, Object> methodResultCache = cacheMap.computeIfAbsent(colOperation.getClass(), clazz -> new SafeConcurrentHashMap<>());
                        return methodResultCache.computeIfAbsent(method, methodKey -> {
                            try {
                                return method.invoke(colOperation, args);
                            } catch (Exception e) {
                                return null;
                            }
                        });
                    }
                }
        );
        return (T) proxyInstance;
    }

    /**
     * 匹配操作
     * @param psiMethod
     * @param colOperations
     * @param predicate
     * @return
     * @param <T>
     */
    public static <T extends IColOperation> T matchOperation(PsiMethod psiMethod, Collection<T> colOperations, Predicate<T> predicate){
        Project project = psiMethod.getProject();
        PsiClass containingClass = psiMethod.getContainingClass();

        for (T colOperation : colOperations) {
            PsiClass psiClass = PsiClassUtils.findPsiClass(project, colOperation.getClassQualifiedName());
            if (!PsiClassUtils.isInheritorOrEquals(containingClass,psiClass)){
                continue;
            }
            if (predicate.test(colOperation)){
                return colOperation;
            }
        }

        return null;
    }
}
